﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenQuarters.WebQuarters.Utils;
using System.ComponentModel;

namespace OpenQuarters.WebQuarters.Core
{
    [Serializable]
    public class Filter
    {
        private string id = null;

        public string Id
        {
            get
            {
                if (id == null)
                {
                    id = Utils.GeneralUtils.GetGuid();
                }
                return id;
            }
            set { id = value; }
        }

        public string Field { get; set; }
        public string Operator { get; set; }
        public string Value { get; set; }
        /// <summary>
        /// This field has been depreciated, but has been maintained for backwards compatibility (as of v1.0.4)
        /// </summary>
        public Type Type { get; set; }

        public FilterOperator OperatorName
        {
            get
            {
                switch (this.Operator)
                {
                    case "=":
                        return FilterOperator.Equals;
                    case "!=":
                        return FilterOperator.Does_not_equal;
                    case ">":
                        return FilterOperator.More_than;
                    case ">=":
                        return FilterOperator.More_than_or_equal_to;
                    case "<":
                        return FilterOperator.Less_than;
                    case "<=":
                        return FilterOperator.Less_than_or_equal_to;
                    case "%":
                        return FilterOperator.Contains;
                    case "!%":
                        return FilterOperator.Does_not_contain;
                    case "IN":
                        return FilterOperator.In;
                    default:
                        return FilterOperator.Equals;
                }
            }
        }

        public string OperatorNameAsString
        {
            get
            {
                return OperatorName.ToString();
            }
        }

        public Filter()
        {
            this.Operator = "=";
            this.Value = "";
            //this.Type = typeof(string);
        }

        public Filter(string field, string value, string filterOperator)
        {
            this.Operator = filterOperator;
            this.Value = value;
            this.Field = field;
            //this.Type = type;
        }

        public bool Passes(object o)
        {
            bool b = false;

            var prop = o.GetType().GetProperty(this.Field);
            if (prop == null)
            {
                return b;
            }

            var objValue = prop.GetValue(o, null);
            var valueType = prop.PropertyType;
            var valueTypeName = valueType.FullName;
            if (valueTypeName.Contains("System.DateTime") && this.Value.StartsWith("Now"))
            {
                this.Value = DateTime.Now.ToString("O");
            }
            if (valueTypeName.Contains("System.DateTime"))
            {
                valueTypeName = "System.DateTime";
            }
            if (valueType.IsGenericType && valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)) && objValue != null)
            {
                objValue = new NullableConverter(valueType).ConvertTo(objValue, valueType.GetGenericArguments()[0]);
            }

            try
            {
                switch (this.Operator)
                {
                    case "=":
                        switch (valueTypeName)
                        {
                            case "System.String":
                            case "System.Int16":
                            case "System.Int32":
                            case "System.Int64":
                                b = objValue.ToString() == this.Value;
                                break;
                            case "System.DateTime":
                                if (objValue == null)
                                {
                                    b = false;
                                }
                                else
                                {
                                    b = ((DateTime)objValue).ToUniversalTime().ToString("U") == DateTime.Parse(this.Value).ToUniversalTime().ToString("U");
                                }
                                break;
                            case "System.Boolean":
                                b = ((bool)objValue) == Boolean.Parse(this.Value);
                                break;
                        }
                        break;
                    case "!=":
                        switch (valueTypeName)
                        {
                            case "System.String":
                            case "System.Int16":
                            case "System.Int32":
                            case "System.Int64":
                                b = objValue.ToString() != this.Value;
                                break;
                            case "System.DateTime":
                                if (objValue == null)
                                {
                                    b = false;
                                }
                                else
                                {
                                    b = ((DateTime)objValue).ToUniversalTime().ToString("U") != DateTime.Parse(this.Value).ToUniversalTime().ToString("U");
                                }
                                break;
                            case "System.Boolean":
                                b = ((bool)objValue) != Boolean.Parse(this.Value);
                                break;
                        }
                        break;
                    case ">":
                        switch (valueTypeName)
                        {
                            case "System.Int16":
                            case "System.Int32":
                            case "System.Int64":
                                b = objValue.ToString().ToInt64() > this.Value.ToInt64();
                                break;
                            case "System.DateTime":
                                if (objValue == null)
                                {
                                    b = false;
                                }
                                else
                                {
                                    b = ((DateTime)objValue).ToUniversalTime() > DateTime.Parse(this.Value).ToUniversalTime();
                                }
                                break;
                        }
                        break;
                    case "<":
                        switch (valueTypeName)
                        {
                            case "System.Int16":
                            case "System.Int32":
                            case "System.Int64":
                                b = objValue.ToString().ToInt64() < this.Value.ToInt64();
                                break;
                            case "System.DateTime":
                                if (objValue == null)
                                {
                                    b = true;
                                }
                                else
                                {
                                    b = ((DateTime)objValue).ToUniversalTime() < DateTime.Parse(this.Value).ToUniversalTime();
                                }
                                break;
                        }
                        break;
                    case ">=":
                        switch (valueTypeName)
                        {
                            case "System.Int16":
                            case "System.Int32":
                            case "System.Int64":
                                b = objValue.ToString().ToInt64() >= this.Value.ToInt64();
                                break;
                            case "System.DateTime":
                                if (objValue == null)
                                {
                                    b = false;
                                }
                                else
                                {
                                    b = ((DateTime)objValue).ToUniversalTime() >= DateTime.Parse(this.Value).ToUniversalTime();
                                }
                                break;
                        }
                        break;
                    case "<=":
                        switch (valueTypeName)
                        {
                            case "System.Int16":
                            case "System.Int32":
                            case "System.Int64":
                                b = objValue.ToString().ToInt64() <= this.Value.ToInt64();
                                break;
                            case "System.DateTime":
                                if (objValue == null)
                                {
                                    b = true;
                                }
                                else
                                {
                                    b = ((DateTime)objValue).ToUniversalTime() <= DateTime.Parse(this.Value).ToUniversalTime();
                                }
                                break;
                        }
                        break;
                    case "%":
                        switch (valueTypeName)
                        {
                            case "System.String":
                            case "System.Int16":
                            case "System.Int32":
                            case "System.Int64":
                                b = objValue.ToString().Contains(this.Value);
                                break;
                        }
                        break;
                    case "!%":
                        switch (valueTypeName)
                        {
                            case "System.String":
                            case "System.Int16":
                            case "System.Int32":
                            case "System.Int64":
                                b = !objValue.ToString().Contains(this.Value);
                                break;
                        }
                        break;
                }
            }
            catch (FormatException) { }
            catch (ArgumentException) { }

            return b;
        }

        public object GetValueAsObject(Type objectType)
        {
            object valueObj = null;
            var prop = objectType.GetProperty(this.Field);
            var valueType = prop.PropertyType;
            var valueTypeName = valueType.FullName;

            if (valueTypeName.Contains("System.DateTime"))
                valueTypeName = "System.DateTime";

            try
            {
                switch (valueTypeName)
                {
                    case "System.String":
                        valueObj = this.Value;
                        break;
                    case "System.Int16":
                        valueObj = Convert.ToInt16(this.Value);
                        break;
                    case "System.Int32":
                        valueObj = Convert.ToInt32(this.Value);
                        break;
                    case "System.Int64":
                        valueObj = Convert.ToInt64(this.Value);
                        break;
                    case "System.DateTime":
                        if (this.Value.ToUpper().StartsWith("GETDATE()") || this.Value.ToUpper().StartsWith("NOW") || this.Value.ToUpper().StartsWith("[NOW]"))
                            if (valueType.FullName.Contains("System.Nullable"))
                            {
                                valueTypeName = "System.Nullable<System.DateTime>";
                                System.Nullable<DateTime> now;
                                valueObj = new Nullable<DateTime>(DateTime.Now);
                            }
                            else
                                valueObj = DateTime.Now;
                        break;
                    case "System.Boolean":
                        valueObj = Boolean.Parse(this.Value);
                        break;
                }
            }
            catch { }

            return valueObj;
        }
    }
}
